\chapter{声明}

在Tenon语言中，声明和定义是合一的，两个词语可以互换。声明包括以下几类：

\begin{itemize}
    \item 模块声明
    \item 类型声明
	\item 变量声明
	\item 函数声明
\end{itemize}

\section{基本数据类型}

Tenon包含三种基本数据类型：\verb!布尔类型!、\verb!数值类型!和\verb!字符串类型!。
基本数据类型是Tenon的组成部分，在任何模块中的任何位置都可使用，不需要进行声明。

\subsection{布尔类型}

Tenon的布尔类型变量占据1字节大小的空间。布尔类型只存在两种可能的取值，
分别是代表真值的true和代表假值的false。内部实现中，true被存储为编码1，false被存储为编码0。
若声明布尔类型的变量时没有定义变量的值，则其实际值为false。

布尔类型变量的示例：

\begin{verbatim}
    bool val;
    val = true;

    bool bval = false;
\end{verbatim}

\subsection{数值类型}

Tenon语言中的数值类型变量所占的空间大小，和实现Tenon语言的平台有关。在一些精度要求不高、
性能较低的平台上，数值类型实现为单精度浮点数，占据4字节大小的空间；
在精度要求较高的平台上，数值类型实现为双精度浮点数，占据8字节大小的空间。
浮点数的存储采用IEEE 754标准。
若声明数值类型的变量时没有定义变量的值，则其实际值为0。

需要注意的是，并不是所有的数值都可以被表示为计算机中的浮点数。
有些数值（例如4.2）在计算机中的内部存储和其实际值会存在一定的偏差。因此，
在比较两个数值时，不应当使用==运算符。应该给定一个容错值，当进行比较的两个数值的差值小于此容错值时，
即认为两个数值是相等的。

数值类型变量的示例：

\begin{verbatim}
    num val;
    val = 3.14;

    num nval = 3.14;
\end{verbatim}

\subsection{字符串类型}

Tenon语言的字符串类型变量所占的空间大小是不固定的，取决于其实际的字符串的值。

字符串类型变量的示例：

\begin{verbatim}
    string val;
    val = "hello";

    string sval = "hello world";
\end{verbatim}

\subsection{其他内置类型}
除了以上基本数据类型之外，Tenon还内置了一些其他的数据类型，用于定义一些特定变量。

\subsubsection{intnum}
intnum是num的别名类型，用于定义中断变量。

\subsubsection{errnum}
errnum是num的别名类型，用于定义错误编号。

\vspace{2ex}

\noindent \textbf{模块体}
一个项目即一个模块，是一对一的关系
模块体也即子程序

\begin{verbatim}
module_body = module_body_item | module_body module_body_item
module_body_item = module_specifier_opt type_definition
                 | module_specifier_opt module_var_decl
                 | module_specifier_opt alias_decl
                 | module_specifier_opt func_decl
module_specifier_opt = empty | LOCAL
\end{verbatim}

子程序中包含一系列的项，以定义类型、声明变量、声明别名或者添加函数等。
每一项都包含一个可选的作用域说明符local，当使用此说明符时，
该项被声明为只在模块内部可见；不使用时，则该项可以被其它模块使用。

\vspace{2ex}

\noindent \textbf{新架构下的项目与子程序布局}

在新版Tenon项目架构中，module即为项目，module内的每item对应一个\texttt{.t}文件，即为子程序。

\texttt{项目结构示例：}
\begin{verbatim}
root/
  |-- project1/       # 项目1
  |     |-- funcA.t   # 子程序1
  |     |-- funcB.t   # 子程序2
  |
  |-- project2/       # 项目2
        |-- main.t    # 子程序1
        |-- demo.t    # 子程序2
\end{verbatim}

\section{类型声明}

在Tenon语言中，如果内置类型不能满足需求，还可以添加四种用户定义数据类型：
记录类型、别名类型、枚举类型和隐含类型。
在记录类型中，甚至可以包含函数成员。

类型声明只能出现在模块中，不能出现在函数中。

\vspace{2ex}

\noindent \textbf{语法}

\begin{verbatim}
type_definition = record_definition | enum_definition | opaque_definition
\end{verbatim}

\subsection{记录类型}

在记录类型中，可以包含不同数据类型的数据项。
例如，对于超市管理系统，对商品的描述可能需要以下几项：商品名、产地、商品ID、单价等。
分别需要用字符串、字符串、数值、数值来存储相应数据。

\subsubsection{语法}

\begin{verbatim}
record_definition = RECORD record_type LBRACE record_field_list RBRACE
record_type = IDENTIFIER
record_field_list = record_field | record_field_list record_field
record_field = data_field_definition | func_decl
data_field_definition = data_type IDENTIFIER ';'
\end{verbatim}

\subsubsection{声明记录类型}

记录类型的声明以record关键字和记录名开头，后跟记录的记录体，记录体以\{开始，以\}结束。
在记录体中，是该记录类型所包含的成员的列表。

在定义记录类型时必须给出记录类型的类型名，Tenon不支持定义和使用匿名记录类型。
记录名需要是一个合法的标识符，且不能与任务中其它符号的名称冲突。

记录类型中的成员又称为\verb!记录类型的域!，既可以是数据成员，也可以是函数成员。
定义记录类型中的数据成员时，需要给出该数据成员的类型名以及该数据成员的名称。
记录类型中的数据成员不能加数组修饰符。

记录类型中数据成员类型可以是记录类型，但不能是其自身，
也不能是包含了该类型的其它记录类型。例如，记录类型A包含了记录类型B，
记录类型B包含了记录类型C，那么，在C中不能包含记录类型A、记录类型B、记录类型C。

记录类型的函数成员和普通的函数类似，详细内容请参考``函数声明"一节。

\subsubsection{声明记录类型示例}

\begin{verbatim}
    record Goods {
        string name;
        string origin;
        num id;
        num price;
    }
\end{verbatim}

以上示例定义了一个名为Goods的记录类型，该类型包含name、origin、id、price等4个数据成员。
该类型不包含函数成员。

\subsubsection{声明记录类型的变量}

在定义一个记录类型之后可以声明此类型的变量。
有以下三种方式可以初始化一个记录类型的变量：

\begin{itemize}
	\item 在声明时初始化
	\item 在声明后初始化
	\item 在声明后，对成员进行初始化
\end{itemize}

对记录类型变量进行初始化的方法是依次给出该记录类型各数据成员的值，中间用逗号隔开，
所有的值用中括号包含。

如果声明记录类型的变量时没有初始化，则其每个数据成员都会有一个默认的初始值，
且其初始值和该数据成员的类型有关。例如，对于类型为数值类型的数据成员，会有默认的初始值0；
字符串类型的数据成员会有默认的初始值\textquotedbl\textquotedbl；
布尔型的数据成员会有默认的初始值false。

在引用记录类型的成员时，需要同时给出记录类型变量的变量名，变量名和成员名之间使用句点\verb|.|隔开。
对于包含了记录类型的记录类型，需要依次给出所引用的成员名，中间用句点\verb|.|隔开。

例如，在定义记录类型Goods之后，可以使用以下的语句：
\begin{verbatim}
    Goods pen;
    pen.name = "ChenGuang";
    pen.origin = "ShangHai";
    pen.id = 10101;
    pen.price = 0.99;

    Goods book;
    book = { "Learn Tenon", "China", 10001, 30.0 };

    Goods apple = { "FuShi", "China", 11001, 5.7 };
\end{verbatim}

上面的示例用了三种方式对声明的三个Goods类型变量进行了初始化。


\subsubsection{复杂示例}

\begin{verbatim}
    record pos {
        num x;
        num y;
        num z;
    }

    record orient {
        num q1;
        num q2;
        num q3;
        num q4;
    }

    record pose {
        pos trans;
        orient rot;
    }

    pose p1 = { {1, 2, 3}, {1, 0, 0, 0} };
    p1.trans.x = 2;
    p1.rot = { 0.96592582628907, 0, 0.25881904510252, 0 };

\end{verbatim}

\subsection{枚举类型}

一个用户声明的枚举类型给出一组可能值的集合，其变量只能从这些可能值的集合中取值，
因此，使用枚举类型可以防止用户给出无效值。Tenon语言的枚举类型变量占据4字节大小的空间。


\subsubsection{语法}

\begin{verbatim}
enum_definition = ENUM enum_type LBRACE enum_member_list comma_opt RBRACE
enum_member_list = enum_member | enum_member_list ',' enum_member
enum_member = IDENTIFIER | IDENTIFIER '=' IDENTIFIER | IDENTIFIER '=' NUMBER_LITERAL
            | IDENTIFIER '=' '-' NUMBER_LITERAL
comma_opt = empty | ','
\end{verbatim}

\subsubsection{声明枚举类型}

枚举类型的声明以enum关键字和枚举类型的类型名开头，后跟该枚举类型的枚举体。枚举体以\{开始，以\}结束。
在枚举体中，是该枚举类型的所有可能值的名称的列表，每个列表项称为该\verb!枚举类型的成员!。

在声明枚举类型时必须给出枚举类型的类型名，Tenon不支持声明和使用匿名枚举类型。
枚举名需要是一个合法的标识符，且不能与任务中其它符号的名称冲突。

可以在声明枚举类型时为其每个成员赋予一个整数值，此值即存储该枚举类型变量时的实际编码。
若在声明枚举类型时没有为成员指定值，则其值或者为0（第一个成员），
或者为其上一个成员的值加1（其它成员）。

可以在声明枚举类型时将某个成员的值赋予其它成员，此时，两个成员有相同的实际值。

只能在声明枚举类型时指定其成员的实际值。


\subsubsection{声明枚举类型示例}

\begin{verbatim}
    enum Color {
        White,
        Black
    }
\end{verbatim}

以上代码定义了一个名为Color的枚举类型，该枚举类型包含两个成员White和Black，
其实际的值分别为0和1。Color类型的变量的值只能是White或Black。

再比如：
\begin{verbatim}
    enum Fruit {
        Apple = 1000,
        Banana,
        Cherry,
        Grape,
    }
\end{verbatim}

以上代码定义了一个名为Fruit的枚举类型，该枚举类型包含4个成员Apple、Banana、Cherry、Grape，
其实际的值分别为1000、1001、1002、1003。

\subsubsection{声明枚举类型的变量}

在声明一个枚举类型之后可以声明此类型的变量。例如，在声明枚举类型Color之后，可以使用以下的语句：
\begin{verbatim}
    Color color;
    color = Color::Black;

    Color dark = Color::Black;
\end{verbatim}

在引用枚举类型的成员时，需要同时给出枚举类型的类型名，类型名和成员名之间使用成员分割符::隔开。

需要注意的是，虽然枚举类型成员的实际值为数值类型，但枚举类型和数值类型相互之间不能替换使用。
即数值类型变量不能被赋予枚举类型成员值，枚举类型变量不能被赋予数值类型值。

\subsection{隐含类型}

隐含类型是使用语言扩展实现的自定义数据类型，隐含类型对Tenon语言用户隐藏了其内部实现，
并且通常情况下可以实现更高的性能。

\subsubsection{语法}

\begin{verbatim}
opaque_definition = OPAQUE IDENTIFIER ';'
\end{verbatim}

隐含类型的声明以关键字opaque开头，后跟其类型名，最后以分号结尾。

\section{变量声明}

变量按作用域可分为共享变量、模块变量和函数变量。
共享变量是可以被多个任务同时使用，不会造成访问冲突的变量。
模块变量按存储类型又可分为普通变量、常变量、持久变量。
函数变量按存储类型可分为普通变量和常变量。
在不引起歧义的情况下，普通变量、常变量、持久变量也会称为变量、常量、持久量。

\subsubsection{语法}

\begin{verbatim}
模块变量声明
module_var_decl = shared_decl | const_decl | var_decl | pers_decl
函数变量声明
decl_stmt = const_decl | var_decl
\end{verbatim}

\subsection{共享变量}

共享变量是在任务间共享的系统变量，且不同任务对共享变量的读取和修改是互斥的。
共享变量必须在模块中声明。


\subsubsection{语法}

\begin{verbatim}
shared_decl = SHARED data_type IDENTIFIER initializer_opt ';'

initializer_opt = empty | '=' expr
\end{verbatim}

共享变量的声明以关键字shared开头，后跟共享变量的类型及名称。
不同任务对同一共享变量都必须进行声明，且声明必须完全一样。

共享变量可以包含初始化表达式，如果没有包含初始化表达式，则初始值为默认初始值。

共享变量不能用普通运算符进行读写，必须使用专门的函数进行读写。

\subsubsection{初始化表达式}
表达式必须为常量表达式（表达式中不包含任何变量）。初始化表达式的类型必须和变量的类型兼容。
如果为内置类型或枚举类型，则初始化表达式的类型也必须为相应的类型；
如果为数组或者记录类型，则初始化表达式必须是聚合表达式，且聚合表达式被视为相应的数组或者记录类型。

\subsubsection{基本数据类型的例子}

task1

\begin{verbatim}
shared num svn = 100;

void main() {
    waittime(0.5);
    shared_num_add(svn, 10.0);
    print("svn: {}\n", shared_num_read(svn));
}
\end{verbatim}

task2

\begin{verbatim}
shared num svn = 100;

void main() {
    waittime(1.0);
    shared_num_add(svn, 1.0);
    print("svn: {}\n", shared_num_read(svn));
}
\end{verbatim}

\subsubsection{记录类型的例子}

task1

\begin{verbatim}
shared XYZ abc = {1, 2, 3};

void main() {
    waittime(0.5);
    XYZ tabc;
    shared_sync(tabc, abc);
    tabc.a = 100;
    shared_sync(abc, tabc);

    XYZ res;
    shared_sync(res, abc);
    print("abc: {}\n", res);
}
\end{verbatim}

task2

\begin{verbatim}
shared XYZ abc = {1, 2, 3};

void main() {
    waittime(1.0);
    XYZ tabc;
    shared_sync(tabc, abc);
    tabc.c = 300;
    shared_sync(abc, tabc);

    XYZ res;
    shared_sync(res, abc);
    print("abc: {}\n", res);
}
\end{verbatim}

\subsection{普通变量}

普通变量是在声明后既能够读取，又能够改变其值的变量。

\subsubsection{语法}

\begin{verbatim}
var_decl = data_type IDENTIFIER array_suffix_opt initializer_opt ';'

data_type = primitive_type | IDENTIFIER
primitive_type = NUM | BOOL | STRING

array_suffix_opt = empty | dim_expr_list
dim_expr_list = dim_expr | dim_expr_list dim_expr
dim_expr = '[' expr ']'

\end{verbatim}

普通变量的声明以变量的数据类型和变量名开头，
以及可选的数组修饰符和可选的初始化表达式，最后以分号结尾。

变量可以被声明为基本数据类型（布尔型、数值型、字符串型）或自定义类型，
自定义类型根据标识符进行确定。

\subsection{常变量}

常变量是在声明时给定其值，之后只能读取其值的变量。

\subsubsection{语法}

\begin{verbatim}
const_decl = CONST data_type IDENTIFIER array_suffix_opt '=' expr ';'
\end{verbatim}

常变量的声明以关键字const开头，后跟该变量的数据类型和变量名，
以及可选的数组修饰符，后跟初始化表达式，最后以分号结尾。

常变量声明中必须包含初始化表达式。

\subsection{持久变量}

持久变量是保存在数据库中的变量。持久变量必须在模块中声明。
在模块被加载时，若数据库中没有此变量，则计算初始化表达式的值作为初始值并在程序执行结束的时候将其写入数据库；
若数据库中有此变量，则从数据库中读取值并作为初始值。
在任务执行的过程中，也可以通过调用内置库中的\verb|load_pers| 和 \verb|store_pers|扩展函数，手动进行持久变量在数据库中的读取和存储。

持久变量分为三种，分别用于任务间、任务内和模块内。任务间是指多个任务都可以读取和存储该持久变量；
任务内是指单个任务内的所有模块都可以读取和存储该持久变量；模块内是指只有在当前模块内能够对该持久变量进行读取和存储。

不同任务内的同名持久变量和不同模块内的同名持久变量不会相互影响。

无论是多任务执行还是单任务执行，三种持久变量都用同一个数据库进行持久变量的存储。

\subsubsection{语法}

\begin{verbatim}
    pers_decl = PERS data_type IDENTIFIER array_suffix_opt initializer_opt ';'
\end{verbatim}

持久变量的声明以关键字pers开头，后跟该变量的数据类型和变量名，
以及可选的数组修饰符和可选的初始化表达式，最后以分号结尾。

持久变量的初始化表达式必须为字面量表达式。

\subsubsection{例子}

\begin{verbatim}
shared pers num a;
pers bool b = false;
local pers string c = "tenon";
void main() {
    load_pers(a);
    a = 10;
    store_pers(a);
}
\end{verbatim}

\subsection{数组修饰符}
当在变量名后包含有数组修饰符时，表示声明一个数组。数组修饰符是由中括号包括的表达式列表，其中可以
包括1至3个表达式，用于指定数组每个维度的大小。

注意：数组修饰符修饰的是变量，即如果加了数组修饰符，则表示声明了一个数组变量。因此，不存在数组类型。
所以函数不能返回数组值，record类型不能包含数组成员。函数参数可以接受数组，因为函数参数是变量声明。

例如：

\begin{verbatim}
    num arr1[4] = {1, 2, 3, 4};
    num arr2[2][3] = {{1, 2, 3}, {4, 5, 6}};
\end{verbatim}

在上面的示例中，arr1为1维数组, 包含4个元素。arr2是2维数组，第1维的大小为2，第2维大小为3。

数组变量可以整体赋值，整体赋值的数组变量维度必须相同。例如：

\begin{verbatim}
    num arr1[4] = {1, 2, 3, 4};
    num arr2[4];
    arr2 = arr1;
\end{verbatim}

\section{函数声明}

\subsection{普通函数}

普通函数区别于下面的陷阱函数，可以被其它函数调用。
普通函数又可以分为\verb!隐含函数!和\verb!非隐含函数!，其中非隐含函数是用Tenon语言代码编写的函数，
而隐含函数是用C语言等其它语言编写的函数。
通常，隐含函数需要被编译为二进制代码，在调试时无法看到其代码实现（故称为隐含函数）。

\subsubsection{语法}

\begin{verbatim}
func_decl = func_declarator '(' param_list_opt ')' LBRACE func_body RBRACE
            | opaque_func_declarator '(' param_list_opt ')' ';'

opaque_func_declarator = OPAQUE func_declarator
func_declarator = return_type IDENTIFIER
return_type = data_type | VOID
\end{verbatim}

非隐含函数的声明以函数的返回类型和函数名开头，之后是函数的参数列表，后跟函数的函数体。函数的函数体以\{开始，以\}结束。

函数的返回类型不能添加数组修饰符，即函数不能返回一个数组。当返回类型使用关键字void声明时，表示函数可
以没有返回值。

隐含函数的声明以关键字opaque开头，后跟函数的返回类型和函数名，
之后是函数的参数列表，最后以分号结束。声明隐含函数时不能包括函数体。

\subsection{形式参数列表}

函数声明中的参数称为形式参数（或简称形参，对应parameter），
函数调用中的参数称为实际参数（或称为实参，对应argument）。
需要注意的是，函数的参数中不允许包含函数调用。

函数的形式参数列表又可以分为必需参数列表、可选参数列表、
可变长度参数列表（使用符号... 表示，对应语法分析记号为VARGS）三个部分。
如果必需参数列表和可选参数列表同时存在，则可选参数列表必须位于必需参数列表之后；
可变长度参数列表只能存在于必需参数列表之后，不能单独存在；
可选参数列表不能和可变长度参数列表同时使用。

\subsubsection{语法}

\begin{verbatim}
param_list_opt = empty | param_list | param_list ',' VARGS
param_list = param | param_list ',' param
\end{verbatim}

当调用函数时，需要按照函数的形式参数列表给出相应的实际参数列表：

\begin{itemize}
\item 函数声明不包括形式参数列表，相应的，函数调用不包括任何实际参数。

\item 函数声明中包括必需参数列表，相应的，函数调用必须给出其每一个形式参数的实际参数

\item 函数声明中包括可选参数列表，相应的，函数调用可以给出实际参数，也可以不给出。

\item 函数声明中包括可变长度的参数列表，相应的，函数调用中的实际参数列表也是可变长度的。

\end{itemize}


形式参数的语法定义如下：

\subsubsection{语法}

\begin{verbatim}
param = passing_mode param_type IDENTIFIER array_suffix_opt default_value
passing_mode = empty | '&'
param_type = data_type | ANYTYPE
default_value = empty | '=' expr | '=' VOID
\end{verbatim}

可以认为形式参数有以下属性：传值方式、参数类型、是否可选。

\subsubsection{传参方式}

在函数声明中，每个形式参数都可以被声明为\verb!按值传递参数!和\verb!按引用传递参数!，
默认为按值传递参数，或者使用\&符号声明为按引用传递参数。

按值传递参数：运行时会先计算实参表达式的值，执行函数调用时，传递的是该值。

按引用传递参数：实参表达式必须为变量表达式，执行函数调用时，传递的实际是该变量的内存地址。
修改参数的值会改变实参变量的值。


\subsubsection{参数类型}

形式参数的参数类型除基本数据类型和自定义数据类型外，还可以是一种特殊的类型：\verb!任意类型!，用关键
字anytype表示。当形式参数声明为任意类型时，实际参数可以是任意类型的表达式。 anytype类型的参数只能用
在隐含函数中，普通用户编写Tenon函数的时候用不到anytype。在大多数情况下，anytype参数都以引用方式传
递，即参数声明为anytype\&。

函数参数允许添加数组修饰符。数组修饰符的维度允许由1到3个*表示。符号*表示该维大小信息由实际参数给出。

例子：

\begin{verbatim}
void f(num x[*]) {
    print("%n", x[1]);
}

void main() {
    num a[10];
    f(a);
}
\end{verbatim}

当将数组变量a传给函数f时，f的参数x的维度就是\verb|num [10]|.

\subsubsection{可选参数}

如果形式参数有默认值，则称为可选参数。在调用包含有可选参数的函数时，如果给出了可选参数的实际参数，则
称为\verb!可选参数存在!，函数调用时传递的是实际参数的值；否则，函数调用时传递的是形式参数的默认值。
可以在函数中使用present库函数判断可选参数是否存在。

指定可选参数的实际参数值时，需要提供参数的名字，如y = 1。可选参数的指定顺序不需要和其声明的顺序相
同。

可选参数的默认值可以为void，此时，默认值的实际值相当于所有内存内容被置为0.

\subsection{函数体}

函数体中包含了函数的可执行代码，是由语句列表组成的。函数体中首先是函数的一般可执行代码块，后面可以
有error代码块，包含函数错误处理器。 error代码块是可选的。 error代码块最多出现一次。

\subsubsection{语法}

\begin{verbatim}
func_body = stmt_list_opt | func_body func_block
func_block = ERROR errno_list_opt stmt_list_opt
errno_list_opt = empty | '(' expr_list ')'
\end{verbatim}

error代码块的声明以error关键字开头，后跟可选的错误码表达式列表，之后是其可执行代码的语句列表。

error后如果没有错误码列表，则会捕获所有错误。若最后没有用return, raise, retry, trynext处理错误，则会
出错。

error后如果有错误码列表，只捕获出现在列表中的错误。其余错误会自动raise。
对捕获的错误，若最后没有用return, raise, retry, trynext处理错误，则也会向上传播。

\subsection{陷阱函数}

陷阱函数是可以作为中断处理程序的特殊函数。陷阱函数既没有参数列表，也没有返回值（返回值类型为void）。
可以使用iconnect扩展函数将一个中断和一个陷阱函数进行绑定，
之后当有此类型的中断发生时，和其绑定的陷阱函数会被Tenon执行器执行。

可以将多个中断绑定到同一个陷阱函数。

\subsection{函数属性修饰}

在Tenon语言中，可以通过\texttt{@property}属性修饰器为函数声明附加额外的属性信息。属性修饰器需写在函数声明前，格式如下：

\begin{verbatim}
@property("属性1", "属性2", ...)
返回类型 函数名(参数列表);
\end{verbatim}

常见属性说明：
\begin{itemize}
    \item \texttt{opaque}：声明该函数为隐含函数。
    \item \texttt{motion}：声明该函数为运动指令。
    \item \texttt{overloadable}：声明该函数为可重载的函数，即可声明多个同名函数(参数个数或参数类型不同，且可重载的函数参数类型不能为anytype)。
    \item \texttt{interruptible}：只适用于扩展函数，声明该扩展函数在执行时可监听和处理中断。
\end{itemize}

\texttt{示例：}
\begin{verbatim}
@property("overloadable") void demo(num a) { print("{}\n", a); }
@property("overloadable") void demo(num a, bool b) { print("{}, {}\n", a, b); }
@property("opaque", "motion", "interruptible") void func3();
\end{verbatim}

\subsection{编译时重载函数的匹配过程}

编译时，会以下面的流程查找符号对应的函数（包括重载函数）：

\begin{itemize}
    \item \texttt{1.}：在模块的函数表中查找同名函数。
    \item \texttt{2.}：在任务的符号表中查找同名函数。
    \item \texttt{3.}：函数调用时，在符号表中进行查找对应的重载函数。
    \item \texttt{4.}：在模块的符号表中，根据重载匹配算法查找与函数名匹配的重载函数。
\end{itemize}

在查找重载函数时，如果存在可以精确匹配的重载函数，则首先匹配此函数。例如：


\begin{verbatim}
@property("overloadable")
void moveC(robjoint rj, robtarget rt) {
    print("moveC(robjoint rj, robtarget rt)\n");
}

@property("overloadable")
void moveC(robjoint rj, robtarget rt, robtarget rt2 = void, robtarget rt3 = void) {
    print("moveC(robjoint rj, robtarget rt, robtarget rt2 = void, robtarget rt3 = void)\n");
}

\end{verbatim}

当调用

\begin{verbatim}

moveC(rj, rt);

\end{verbatim}

时，会优先匹配不包含可选参数的重载函数。

\section{别名}

可以声明全局变量或模块变量的变量别名，也可以声明全局类型或模块类型的类型别名，然后使用该别名替代原名称的使用。
别名必须在模块中进行声明。

\begin{verbatim}
alias_decl = ALIAS primitive_type IDENTIFIER ';'
             | ALIAS IDENTIFIER IDENTIFIER ';'
\end{verbatim}

别名的声明以关键字alias开头，后跟别名的实际名称，以及替代使用的别名，最后以分号结尾。

\section{作用域规则}

按作用域划分，Tenon中的类型分为任务类型、模块类型；变量分为共享变量、任务变量、模块变量、局部变量；
函数分为任务函数、模块函数。

任务类型是指在定义类型时，未使用作用域说明符local进行声明的类型。
模块类型则与之相反，是指在定义类型时，使用作用域说明符local进行声明的类型。

共享变量是指在定义变量时，使用shared关键字进行声明的变量。
任务变量是指除共享变量外，在定义变量时未使用作用域说明符local进行声明的变量。
模块变量是指除共享变量外，在定义变量时使用作用域说明符local进行声明的变量。
局部变量是指定义在函数内部的变量。

任务函数是指在定义类型时，未使用作用域说明符local进行声明的函数。
模块函数是指在定义类型时，使用作用域说明符local进行声明的函数。


下表总结了可见性规则。
\begin{center}
\begin{tabular}{ |c|c| } 
    \hline
        对象种类 & 可见性  \\
    \hline
        任务类型 & 任务内、模块间可见 \\
        模块类型 & 模块内可见 \\
    \hline
        共享变量 & 任务间可见 \\
        任务变量 & 任务内、模块间可见 \\
        模块变量 & 模块内可见 \\
        局部变量 & 函数内可见 \\
    \hline
        任务函数 & 任务内、模块间可见 \\
        模块函数 & 模块内可见 \\
    \hline
\end{tabular}
\end{center}

同一个任务中的不同模块不可以定义同名的任务类型、任务变量、任务函数。
一个模块里定义的任务类型、任务变量、任务函数在同一个任务中的其他模块中可以直接使用，
无需考虑模块加载的顺序。

模块里定义的模块类型、模块变量、模块函数只能在本模块内使用。
同一个模块中不可以定义同名的模块类型、模块变量、模块函数。
同一个模块中不可以定义同名的模块类型和任务类型，不可以定义同名的模块变量和任务变量，不可以定义同名的模块函数与任务函数。
在一个模块中定义的模块类型、模块变量、模块函数，可以和同一个任务其它的模块中声明的任务类型、任务变量、任务函数的名称相同。
如果使用了相同的名称，则会使用模块类型、模块变量、模块函数的定义，
相当于其它模块中的任务类型、任务变量、任务函数的定义被本模块中的模块类型、模块变量、模块函数"隐藏"。

局部变量可以和模块变量、任务变量的名称相同。如果使用了相同的名称，则会使用局部变量，而不是模块变量或者任务变量。

局部变量中，子作用域中的局部变量不能和其父作用域中的局部变量同名。
